home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD ROM Paradise Collection 4
/
CD ROM Paradise Collection 4 1995 Nov.iso
/
edit
/
sted102a.zip
/
SUPERTED.ASM
< prev
next >
Wrap
Assembly Source File
|
1994-05-24
|
78KB
|
2,780 lines
;======================================================================
; SuperTED - and enhanced version of
; TED.ASM -- The Tiny EDitor.
; PC Magazine * Tom Kihlken * tktktk
; Enhanced by Tony Whyman, McCallum Whyman Associates Ltd
;----------------------------------------------------------------------
CSEG SEGMENT
ASSUME CS:CSEG, DS:CSEG, ES:CSEG
ORG 100H ;Beginning for .COM programs
START:
JMP BEGIN
;-----------------------------------------------------------------------
; Local data area
;-----------------------------------------------------------------------
MAIL EQU 0
TAB EQU 9
ESC EQU 27
DEL EQU 83
BS EQU 8
CR EQU 13
LF EQU 10
FF EQU 12
MAX_INDENT EQU 80
BUFF_SIZE EQU 65
COLOUR_ATR DB 7
COLOUR_INV DB 0
COLOUR_SRCH DB 0
QUOTE_CHAR DB ">"
IFDEF MAIL
WRAP_FLAG DB 0FFH
ELSE
WRAP_FLAG DB 0
ENDIF
RMARGIN DB 0
EVEN
WCOLUMNS DW 0
COPYRIGHT DB CR,LF,"SuperTED 1.02a - "
DB "An Enhancement of PC Magazine's Tiny Editor",CR,LF
DB "(c) 1988 Ziff Communications Co.",CR,LF
DB "Enhancements (c) 1993, 1994 McCallum Whyman Associates Ltd$",1AH
FILE_TOO_BIG DB "File too big$"
READ_ERR_MESS DB "Read error$"
MEMORY_ERROR DB "Not enough memory$"
MISSING_FILENAME DB "Missing Filename!$"
ABORT_MESS DB "Lose Changes Y/N?",0
DEL_PASTE_MESS DB "Overwrite Paste Buffer Y/N?",0
IN_FILE_MESS DB "Enter Filename?:",0
NO_FILE_MESS DB "Cannot Open File",0
TRUNCATE_MESS DB "WARNING: File truncated",0
IN_READ_ERR DB "Read Error",0
NO_PASTE_MESS DB "Buffer Overflow! Paste Fails",0
PAD_MESS DB " - ",0
CONTINUE_MESS DB "press any key to continue",0
PRINT_CONFIRM DB "Print whole file Y/N?"
HELP_ROW DB 0
HELP_COLS EQU 4
HELP_OFFSETS DB 4,28,44,65
HELP_TEXT DB 0,0,0,0
DB "FUNCTION",0,"KEY",0,"FUNCTION",0,"KEY",0
DB "========",0,"===",0,"========",0,"===",0
DB 0,0,0,0
DB "Abort",0,"Escape",0,"Screen Right",0,"cntl ->",0
DB "Help",0,"F1",0,"Screen Left",0,"cntl <-",0
DB "Undo",0,"F2",0,"Top of File",0,"cntl PGUP",0
DB "Print (selection)",0,"F3",0,"End of File",0,"cntl PGDN",0
DB "Mark",0,"F4",0,"Toggle Insert Mode",0,"INS",0
DB "Cut",0,"F5",0,"Toggle Wrap Mode",0,"ALT-W",0
DB "Paste",0,"F6",0,"Join Lines",0,"ALT-J",0
DB "Paste with Quote",0,"Shift F6",0
DB "Force Word Wrap",0,"ALT-S",0
DB "Exit",0,"F7",0,"Save File",0,"Shift F7",0
DB "Erase to EOL",0,"F8",0,"Replace Text",0,"ALT-L",0
DB "Delete Line",0,"F9",0,0,0
DB "Undelete Line",0,"F10",0,0,0
DB "Find",0,"F11, ALT-F",0,0,0
DB "Find Again",0,"Shift F11, Shift ALT-F",0,0,0
DB "Import File",0,"F12, ALT-I",0,0,0
DB "Import & Quote File",0,"Shift F12, Shift ALT-I",0,0,0
DB "Reformat (selection)",0,"ALT-R",0,0,0
DB 0,0,0,0,-1
IN_FILENAME DB BUFF_SIZE DUP (0)
SAVE_NAME_BUFF DB BUFF_SIZE DUP (0)
PROMPT_STRING DB "1HELP",0,"2UNDO",0,"3PRINT",0
DB "4MARK",0,"5CUT",0,"6PASTE",0,"7EXIT",0
DB "8DEL EOL",0,"9DEL L",0,"10UDEL L",0,0
PROMPT_LENGTH = $ - OFFSET PROMPT_STRING
SAVE_MESS DB "Save as: ",0
DOT_$$$ DB ".$$$",0
DOT_BAK DB ".BAK",0
SRCH_PROMPT DB "SEARCH STRING> ",0
RPL_PROMPT DB "Replace With:",0
SRCH_ERROR DB "Search String not found",0
SRCH_STR DB BUFF_SIZE DUP (0)
RPL_STR DB BUFF_SIZE DUP (0)
SRCH_FLG DB 0
SRCH_STR_END DW 0
INDENT_BUFFER DB MAX_INDENT DUP (0)
IFDEF MAIL
SAVE_FILENAME DB BUFF_SIZE DUP (0)
ENDIF
DIRTY_BITS DB 1
ORGATR DB 7
INVATR DB 070H
SRCHATR DB 0F0H
SAVE_ATR DB 7
KEYREAD_CODE DB 10H
KEYSTATUS_CODE DB 11H
LEFT_MARGIN DB 0
MARGIN_COUNT DB 0
INSERT_MODE DB -1
MARK_MODE DB 0
ROWS DB 23
SAVE_COLUMN DB 0
SAVE_ROW DB 0
QUOTE_FLAG DB 0
LINE_FLAG DB 0
EVEN
INDENT_SIZE DW 0
NAME_POINTER DW 81H
STATUS_REG DW ?
VIDEO_SEG DW 0B000H
LINE_LENGTH DW 0
UNDO_LENGTH DW 0
CUR_POSN DW 0
MARK_START DW 0FFFFH
MARK_END DW 0
MARK_HOME DW 0
TOP_OF_SCREEN DW 0
CURSOR DW 0
LAST_CHAR DW 0
COLUMNSB LABEL BYTE
COLUMNS DW 0
PASTE_SEG DW ?
PASTE_SIZE DW 0
PAGE_PROC DW ?
OLDINT24 DD ?
LAST_WORD DW 0
DISPATCH_TABLE DW OFFSET HELP ,OFFSET UNDO ,OFFSET PRINT
DW OFFSET MARK ,OFFSET CUT ,OFFSET PASTE
DW OFFSET EXIT ,OFFSET DEL_EOL,OFFSET DEL_L
DW OFFSET UDEL_L ,OFFSET BAD_KEY,OFFSET BAD_KEY
DW OFFSET HOME ,OFFSET UP ,OFFSET PGUP
DW OFFSET BAD_KEY ,OFFSET LEFT ,OFFSET BAD_KEY
DW OFFSET RIGHT ,OFFSET BAD_KEY,OFFSET ENDD
DW OFFSET DOWN ,OFFSET PGDN ,OFFSET INSERT
DW OFFSET DEL_CHAR,OFFSET BAD_KEY,OFFSET BAD_KEY
DW OFFSET BAD_KEY ,OFFSET BAD_KEY,OFFSET BAD_KEY
DW OFFSET QUOTE
; The following machine instruction removes the desnow delay. It is
; inserted into the code for EGA, VGA, and MONO displays.
NO_DESNOW = 0EBH + (OFFSET WRITE_IT - OFFSET HWAIT - 2) * 256
;-----------------------------------------------------------------------
; We start by initialize the display, then allocate memory for the file
; and paste segments. Parse the command line for a filename, if one was
; input, read in the file. Finally set the INT 23 and 24 vectors.
;-----------------------------------------------------------------------
BEGIN:
XOR AX,AX
MOV DS,AX ;Get a zero into DS
ASSUME DS:NOTHING
MOV AH,12H
MOV BL,10H ;Get EGA info
INT 10H
CMP BL,10H ;Did BL change?
JE NOT_EGA ;If not, no EGA in system
TEST BYTE PTR DS:[0487H],8 ;Is EGA active?
JNZ NOT_EGA
MOV WORD PTR CS:HWAIT,NO_DESNOW ;Get rid of desnow
MOV AX,DS:[0484H] ;Get number of rows
DEC AL ;Last row is for prompt line
MOV CS:[ROWS],AL ;Save the number of rows
NOT_EGA:
MOV AX,DS:[044AH] ;Get number of columns
MOV CS:COLUMNS,AX ;and store it
MOV BX,WCOLUMNS
OR BX,BX
JNZ DONE_COLUMNS
MOV BH,RMARGIN
MOV CL,8
SAR BX,CL ;extend sign bit
SUB AX,BX
MOV WCOLUMNS,AX
DONE_COLUMNS:
MOV AX,DS:[0463H] ;Address of display card
ADD AX,6 ;Add six to get status port
PUSH CS
POP DS
ASSUME DS:CSEG
MOV STATUS_REG,AX
CMP AX,3BAH ;Is this a MONO display?
JNE COLOUR ;If not, must be a CGA
MOV WORD PTR HWAIT,NO_DESNOW ;Get rid of desnow
JMP SHORT MOVE_STACK
COLOUR:
MOV VIDEO_SEG,0B800H;Segment for colour card
XOR BH,BH ;Use page zero
MOV AH,8 ;Get current attribute
INT 10H
MOV SAVE_ATR,AH ;Save the original attribute
MOV AL,COLOUR_ATR ;Get colour setting
CMP AL,ORGATR ;New Colours?
JE DEFAULT_COLOURS
MOV ORGATR,AL ;Replace monochrome setting
MOV AL,COLOUR_INV
MOV INVATR,AL
MOV AL,COLOUR_SRCH
MOV SRCHATR,AL
JMP SHORT MOVE_STACK
DEFAULT_COLOURS:
MOV ORGATR,AH
XOR AH,077H
MOV INVATR,AH ;Save inverse attributes
XOR AH,80H ;set blinking
MOV SRCHATR,AH
MOVE_STACK:
MOV BX,OFFSET NEW_STACK
MOV SP,BX ;Move the stack downward
ADD BX,15
MOV CL,4 ;Convert program size to
SHR BX,CL ; paragraphs
MOV AH,4AH ;Deallocate unused memory
INT 21H
MOV BX,1000H ;Request 64K for file segment
MOV AH,48H
INT 21H
MOV ES,AX
ASSUME ES:FILE_SEG
MOV AH,48H
INT 21H ;Request 64K for paste buffer
JNC GOT_ENOUGH ;If enough memory, continue
NOT_ENOUGH:
MOV DX,OFFSET MEMORY_ERROR
ERR_EXIT:
PUSH CS
POP DS
MOV AH,9 ;Write the error message
INT 21H ;DOS display service
JMP EXIT_TO_DOS ;Exit this program
GOT_ENOUGH:
MOV PASTE_SEG,AX ;Use this for the paste buffer
GET_FILENAME:
MOV SI,80H ;Point to parameters
MOV CL,[SI] ;Get number of characters
XOR CH,CH ;Make it a word
INC SI ;Point to first character
PUSH SI
ADD SI,CX ;Point to last character
MOV BYTE PTR [SI],0 ;Make it an ASCII string
POP SI ;Get back pointer to filename
CLD
IFDEF MAIL
JCXZ NO_PARAMS
JMP DEL_SPACES
NO_PARAMS:
MOV DX,OFFSET MISSING_FILENAME
JMP ERR_EXIT ;exit
ELSE
JCXZ NO_FILENAME ;If no params, just exit
ENDIF
DEL_SPACES: LODSB ;Get character into AL
CMP AL," " ;Is it a space?
JNE FOUND_LETTER
LOOP DEL_SPACES
FOUND_LETTER:
DEC SI ;Backup pointer to first letter
MOV NAME_POINTER,SI ;Save pointer to filename
MOV DX,SI
MOV AX,3D00H ;Setup to open file
INT 21H
JC NO_FILENAME ;If we can't open, must be new file
FILE_OPENED:
PUSH ES
POP DS ;DS has file segment also
ASSUME DS:FILE_SEG
MOV BX,AX ;Get the handle into BX
XOR DX,DX ;Point to file buffer
MOV AH,3FH ;Read service
MOV CX,0FFFEH ;Read almost 64K bytes
INT 21H
MOV DI,AX ;Number of bytes read in
JNC NO_RD_ERR ;If no error, take jump
MOV DX,OFFSET READ_ERR_MESS
JMP SHORT ERR_EXIT
NO_RD_ERR:
MOV LAST_CHAR,DI ;Save the file size
CMP CX,AX ;Did the buffer fill?
MOV DX,OFFSET FILE_TOO_BIG
JE ERR_EXIT ;If yes, it is too big
MOV AH,3EH
INT 21H ;Close the file
NO_FILENAME:
PUSH ES
PUSH ES ;Save file segment
MOV AX,3524H ;Get INT 24 vector
INT 21H
MOV WORD PTR OLDINT24,BX ;Store the offset
MOV WORD PTR OLDINT24+2,ES;And the segment
PUSH CS
POP DS
MOV DX,OFFSET NEWINT24 ;Point to new vector
MOV AX,2524H ;Now change INT 24 vector
INT 21H
MOV DX,OFFSET NEWINT23
MOV AX,2523H ;Set the INT 23 vector also
INT 21H
POP ES ;Get back file segment
POP DS
ASSUME DS:FILE_SEG, ES:FILE_SEG
CALL REDO_PROMPT ;Draw the prompt line
;check for extended keyboard
MOV AH,5 ;write to ext keyboard buffer
MOV CX,0FFFFH
INT 16H ;write all ones to buffer
MOV CX,10H ;loop up to 16 times
CHECK_EXT:
PUSH CX
MOV AH,10H ;read ext keyboard
INT 16H
POP CX
INC AX
JZ READ_A_KEY ;AX=0 then 0FFFFH read back
LOOP CHECK_EXT
MOV KEYREAD_CODE,0 ;set codes for XT
MOV KEYSTATUS_CODE,1
;-----------------------------------------------------------------------
; Here's the main loop. It updates the screen, then reads a keystroke.
;-----------------------------------------------------------------------
READ_A_KEY:
CMP MARK_MODE,0 ;Is the mark state on?
JE MARK_OFF ;If not, skip this
OR DIRTY_BITS,4 ;Refresh the current row
MOV DX,CUR_POSN
CMP SAVE_ROW,DH ;Are we on the save row?
JE SAME_ROW ;If yes, then redo the row only
MOV DIRTY_BITS,1 ;Refresh the whole screen
SAME_ROW:
MOV AX,CURSOR ;Get cursor location
MOV BX,MARK_HOME ;Get the anchor mark position
CMP AX,BX ;Moving backward in file?
JAE S1
MOV MARK_START,AX ;Switch start and end position
MOV MARK_END,BX
JMP SHORT MARK_OFF
S1:
MOV MARK_END,AX ;Store start and end marks
MOV MARK_START,BX
MARK_OFF:
MOV DX,CUR_POSN
MOV SAVE_ROW,DH
CALL SET_CURSOR ;Position the cursor
TEST DIRTY_BITS,1 ;Look at screen dirty bit
JZ SCREEN_OK ;If zero, screen is OK
MOV AH,KEYSTATUS_CODE ;Get keyboard status
INT 16H ;Any keys ready?
JNZ CURRENT_OK ;If yes, skip the update
CALL DISPLAY_SCREEN ;Redraw the screen
MOV DIRTY_BITS,0 ;Mark screen as OK
SCREEN_OK:
TEST DIRTY_BITS,2 ;Is bottom of screen dirty?
JZ BOTTOM_OK
MOV AH,KEYSTATUS_CODE ;Get keyboard status
INT 16H ;Any keys ready?
JNZ CURRENT_OK ;If yes, skip the update
CALL DISPLAY_BOTTOM ;Redraw bottom of screen
MOV DIRTY_BITS,0
BOTTOM_OK:
TEST DIRTY_BITS,4 ;Is the current line dirty?
JZ CURRENT_OK ;If not, take jump
CALL DISPLAY_CURRENT ;Redraw the current line
MOV DIRTY_BITS,0 ;Mark screen as OK
CURRENT_OK:
MOV AH,KEYREAD_CODE ;Read the next key
INT 16H
CMP SRCH_FLG,0
JE CKEXT
NOT SRCH_FLG
MOV DIRTY_BITS,1
CKEXT:
CMP AL,0 ;Is this an extended code?
JE EXTENDED_CODE
CMP AL,0E0H ;Is this an extended code? (Grey keys)
JE EXTENDED_CODE
CMP AH,0EH ;Was it the backspace key?
JE BACK_SPACE
CMP AH,1 ;Was it an escape?
JNE NOT_ESC
CALL ABORT
JMP READ_A_KEY
NOT_ESC:
CALL INSERT_KEY ;Put this character in the file
JMP READ_A_KEY ;Get another key
BACK_SPACE:
CMP CURSOR,0 ;At start of file?
JE NEXT_KEY ;If at start, can't backspace
CALL LEFT ;Move left one space
CALL DEL_CHAR ;And delete the character
NEXT_KEY:
JMP READ_A_KEY
EXTENDED_CODE:
CALL FUNCTION_DISPATCH
JMP READ_A_KEY
;--------------------------------------------------------------------------------------------
; Analyses scan code from special keys andn jumps to appropriate subroutine
;--------------------------------------------------------------------------------------------
FUNCTION_DISPATCH PROC NEAR
CMP AH,90 ;Is it Shift F7?
JNE NOT_SF7
JMP SAVE_FILE
NOT_SF7:
CMP AH,84H ;Is it control PgUp?
JNE NOT_PAGEUP
JMP TOP
NOT_PAGEUP:
CMP AH,76H ;Is it control PgDn?
JNE NOT_PAGEDOWN
JMP BOTTOM
NOT_PAGEDOWN:
CMP AH,134 ;Is it F12?
JNE NOT_F12
JMP INSERT_FILE
NOT_F12:
CMP AH,136 ;Is it Shift F12?
JNE NOT_SF12
S_ALTI:
NOT QUOTE_FLAG ;Indicate quote required
JMP INSERT_FILE
NOT_SF12:
CMP AH,23 ;Is it ALT-I?
JNE NOT_ALTI
MOV AH,2 ;Test for shift key
INT 16H
TEST AL,3 ;Right or left shift pressed
JNZ S_ALTI
JMP INSERT_FILE
NOT_ALTI:
CMP AH,133 ;Is it f11?
JNE NOT_F11
JMP FIND_STR
NOT_F11:
CMP AH,33 ;Is it ALT-F?
JNE NOT_ALTF
MOV AH,2 ;Test for shift key
INT 16H
TEST AL,3 ;Right or left shift pressed
JNZ S_ALTF
JMP FIND_STR
NOT_ALTF:
CMP AH,135 ;Is it Shift + F11?
JNE NOT_SF11
S_ALTF:
JMP FIND_AGAIN
NOT_SF11:
CMP AH,38 ;Is it ALT-L?
JNE NOT_ALTL
MOV AH,2 ;Test for shift key
INT 16H
TEST AL,3 ;Right or left shift pressed
JNZ S_ALTL
JMP REPLACE
S_ALTL:
JMP RPL_AGAIN
NOT_ALTL:
CMP AH,17 ;Is it an ALT-W?
JNE NOT_ALTW
JMP TOGGLE_WRAP
NOT_ALTW:
CMP AH,19 ;Is it an ALT-R?
JNE NOT_ALTR
JMP REFORMAT
NOT_ALTR:
CMP AH,36 ;Is it an ALT-J?
JNE NOT_ALTJ
JMP JOIN
NOT_ALTJ:
CMP AH,31 ;Is it an ALT-S?
JNE NOT_ALTS
JMP FORMAT_LINE
NOT_ALTS:
CMP AH,74H ;Is it control right arrow?
JNE NOT_SHR
JMP SH_RIGHT
NOT_SHR:
CMP AH,73H ;Is it control left arrow?
JNE NOT_SHL
JMP SH_LEFT
NOT_SHL:
;
; Remaining keys handled through jump table
;
XOR AL,AL
CMP AH,89 ;Skip high numbered keys
JA BAD_KEY
XCHG AH,AL
SUB AL,3BH ;Also skip low numbered keys
JC BAD_KEY
SHL AX,1 ;Make the code an offset
MOV BX,AX ;Put offset in BX
JMP CS:DISPATCH_TABLE[BX] ;Call the key procedure
BAD_KEY:
RET ;Can't dispatch
FUNCTION_DISPATCH ENDP
;-----------------------------------------------------------------------------------------------
; Flips Wrap mode on and off
;-----------------------------------------------------------------------------------------------
TOGGLE_WRAP PROC NEAR
NOT WRAP_FLAG
CALL REDO_PROMPT
RET
TOGGLE_WRAP ENDP
;-----------------------------------------------------------------------
; These two routines shift the display right or left to allow editing
; files which contain lines longer than 80 columns.
;-----------------------------------------------------------------------
SH_RIGHT PROC NEAR
CMP LEFT_MARGIN,255 - 8 ;Past max allowable margin?
JAE NO_SHIFT ;Then can't move any more
ADD LEFT_MARGIN,8 ;This moves the margin over
SH_RETURN:
CALL CURSOR_COL ;Compute column for cursor
MOV DX,CUR_POSN
MOV SAVE_COLUMN,DL ;Save the current column
MOV DIRTY_BITS,1 ;Redraw the screen
NO_SHIFT:
RET
SH_RIGHT ENDP
SH_LEFT PROC NEAR
CMP LEFT_MARGIN,0 ;At start of line already?
JE NO_SHIFT ;If yes, then don't shift
SUB LEFT_MARGIN,8 ;Move the window over
JMP SH_RETURN
SH_LEFT ENDP
;----------------------------------------------------------------------
; This subroutine copies BL to BH and if BL holds lower case graphic then
; makes BH hold upper case equivalent & vice-versa. Otherwise BL=BH
;-----------------------------------------------------------------------
CASE_BLIND PROC NEAR
MOV BH,BL
CMP BH,"A" ;Check to see if before A
JB CASE_DONE
CMP BH,"z" ;Check to see if after z
JA CASE_DONE
CMP BH,"Z" ;Check to see if upper case
JA TEST_LC
ADD BL,32 ;Make BL lower case
RET
TEST_LC:
CMP BH,"a" ;Check to see if lower case
JB CASE_DONE
SUB BH,32 ;Make BH Uppercase
CASE_DONE:
RET
CASE_BLIND ENDP
;-----------------------------------------------------------------------
; This subroutine prompts the user for a search pattern and then searches for
; it in the file from the CURSOR onwards
;-----------------------------------------------------------------------
FIND_STR PROC NEAR
MOV SI,OFFSET SRCH_STR ;Buffer to hold search string
MOV DI,OFFSET SRCH_PROMPT ;Prompt string
MOV CX,BUFF_SIZE ;Max length of search string
CALL READ_STRING ;Prompt user for search string
JNC FIND_END ;Exit if ESC pressed
FIND_AGAIN: ;Entry point for Find Again
MOV SI,CURSOR ;Start search at CURSOR
INC SI ;and one char later
RPT_LOOP:
MOV BL,SRCH_STR ;get 1st char of search string
OR BL,BL ;Is it a zero?
JZ FIND_END ;Null search string
CALL CASE_BLIND ;Make BH to Upper case, BL to lower
MOV CX,LAST_CHAR
SUB CX,SI ;No. of chars to check for match
JCXZ FIND_ERROR ;Exit of EOF
SRCH_LOOP:
LODSB ;Get next char to test
CMP AL,BL ;compare with LC
JE TEST_REST
CMP AL,BH ;Compare with UC
JE TEST_REST
LOOP SRCH_LOOP ;Continue search
FIND_ERROR:
MOV SI,OFFSET SRCH_ERROR ; Report not found
CALL ERROR_MESG
FIND_END:
RET
TEST_REST:
PUSH SI
XOR DI,DI ;DI is search string index
MOV CX,BUFF_SIZE ;Maximum search string
TEST_LOOP:
CMP SI,LAST_CHAR ;EOF yet?
JAE TEST_FAIL
INC DI ;Next character
MOV BL,SRCH_STR[DI]
OR BL,BL ;End of string?
JZ SRCH_END ;String found
CALL CASE_BLIND ;Make BH Upper Case, BL Lower Case
LODSB
CMP AL,CR ;CR Found?
JNE TEST_CHAR
LODSB
CMP AL,LF ;Is this EOL
JNE TEST_FAIL ;Fails if embedded CR
LODSB ;Skip CR/LF
TEST_CHAR:
CMP AL,BL ;Check lower case first
JE TEST_OK
CMP AL,BH ;Then check upper case
JNE TEST_FAIL
TEST_OK:
LOOP TEST_LOOP
TEST_FAIL:
POP SI
JMP SHORT RPT_LOOP ;continue with search
SRCH_END:
MOV SRCH_STR_END,SI ;Save pointer to EOS
POP SI
DEC SI
MOV CURSOR,SI ;Cursor now at start of string
NOT SRCH_FLG ;Indicate need to flash display
MOV DIRTY_BITS,1 ;Redraw screen
MOV UNDO_LENGTH,0 ;reset undo buffer
MOV DX,CUR_POSN ;Locate cursor on current row
CALL LOCATE
RET
FIND_STR ENDP
;-----------------------------------------------------------------------
; This searches for selected text and then replces it with specified
; text
;-----------------------------------------------------------------------
REPLACE PROC NEAR
MOV MARK_MODE,0 ;Mark mode off if replace
CALL FIND_STR
CMP SRCH_FLG,0 ;Found?
JNE RPL_QUERY
RET
RPL_AGAIN:
MOV MARK_MODE,0 ;Mark mode off if replace
CALL FIND_AGAIN
CMP SRCH_FLG,0 ;Found?
JNE RPL_QUERY
RET
RPL_QUERY:
CALL DISPLAY_SCREEN
MOV DI,OFFSET RPL_PROMPT
MOV SI,OFFSET RPL_STR
MOV CX,BUFF_SIZE
CALL READ_STRING ;Get replace string
JC RPL_DO
RET
RPL_DO:
MOV CX,LAST_CHAR
MOV SI,SRCH_STR_END
MOV DI,CURSOR
SUB CX,SI ;Calculate no. of chars to move
MOV AX,CX
ADD AX,DI
MOV LAST_CHAR,AX ;Adjust file length
REP MOVSB ;Delete selected text
NOT SRCH_FLG
MOV SI,OFFSET RPL_STR
MOV AL,INSERT_MODE
MOV INSERT_MODE,-1
PUSH AX
RPL_LOOP: ;insert replacement text
PUSH DS
PUSH CS
POP DS
LODSB
POP DS
CMP AL,0
JE RPL_DONE
PUSH SI
CALL INSERT_KEY
POP SI
JMP SHORT RPL_LOOP
RPL_DONE:
POP AX
MOV INSERT_MODE,AL
RET
REPLACE ENDP
;-----------------------------------------------------------------------
; This moves the cursor to the top of the file.
;-----------------------------------------------------------------------
TOP PROC NEAR
XOR AX,AX ;Get a zero into AX
MOV CURSOR,AX ;Cursor to start of file
MOV TOP_OF_SCREEN,AX
MOV LEFT_MARGIN,AL ;Move to far left margin
MOV DIRTY_BITS,1 ;Redraw the screen
MOV CUR_POSN,AX ;Home the cursor
MOV SAVE_COLUMN,AL ;Save the cursor column
RET
TOP ENDP
;-----------------------------------------------------------------------
; This moves the cursor to the bottom of the file
;-----------------------------------------------------------------------
BOTTOM PROC NEAR
MOV DH,ROWS ;Get screen size
MOV SI,LAST_CHAR ;Point to last character
DEC SI
MOV LEFT_MARGIN,0 ;Set window to start of line
CALL LOCATE ;Adjust the screen position
CALL HOME ;Move cursor to start of line
MOV DIRTY_BITS,1 ;This will redraw the screen
RET
BOTTOM ENDP
;-----------------------------------------------------------------------
; This subroutine is called to reformat remainder of para or selection
;-----------------------------------------------------------------------
REFORMAT PROC NEAR
MOV AL,WRAP_FLAG
PUSH AX
MOV WRAP_FLAG,0FFH ;Wrap flag temporaily on
CMP MARK_MODE,0 ;is wrap mode on?
JE JOIN_NEXT
MOV SI,MARK_START ;start at marked text
MOV CURSOR,SI
JOIN_NEXT:
CALL JOIN ;join this line with the next
CMP MARK_MODE,0
JE REFORMAT_END ;exit if mark mode off
CALL FIND_NEXT_PARA
CMP SI,MARK_END ;end of selection?
JAE REFORMAT_SELECT_END ;then exit
MOV CURSOR,SI
JMP SHORT JOIN_NEXT ;Next para found
REFORMAT_SELECT_END:
MOV SI,MARK_START
MOV DH,3 ;Position on line 3
CALL LOCATE
CALL MARK ;turn off mark mode
REFORMAT_END:
POP AX
MOV WRAP_FLAG,AL
RET
REFORMAT ENDP
;-----------------------------------------------------------------------
; This deletes from the cursor position to the end of line.
;-----------------------------------------------------------------------
DEL_EOL PROC NEAR
MOV CX,CUR_POSN
OR CL,CL ;At first column?
JZ DEL_L ;If yes, then do line delete function
MOV LINE_FLAG,0
PUSH CURSOR ;Save starting cursor location
CALL ENDD ;Move the the end of line
POP SI ;Get back starting cursor
MOV CX,CURSOR ;Offset of the end of line
MOV CURSOR,SI ;Restore starting cursor
JMP DEL_END ;Delete characters to end
DEL_EOL ENDP
;-----------------------------------------------------------------------
; This deletes a line, placing it in the line buffer.
;-----------------------------------------------------------------------
DEL_L PROC NEAR
MOV LINE_FLAG,1
CALL FIND_START ;Find start of this line
MOV CURSOR,SI ;This will be the new cursor
PUSH SI ;Save the cursor position
CALL FIND_NEXT ;Find the next line
MOV CX,SI ;CX will hold line length
POP SI ;Get back new cursor location
DEL_END:
SUB CX,SI ;Number of bytes on line
OR CH,CH ;Is line too long to fit
JZ NOT_TOO_LONG
MOV CX,100H ;Only save 256 characters
NOT_TOO_LONG:
MOV LINE_LENGTH,CX ;Store length of deleted line
JCXZ NO_DEL_L
MOV DI,OFFSET LINE_BUFFER ;Buffer for deleted line
PUSH CX
PUSH ES
PUSH CS
POP ES ;Line buffer is in CSEG
REP MOVSB ;Put deleted line in buffer
POP ES ;Get back file segment
POP AX
MOV CX,LAST_CHAR ;Get the file size
SUB LAST_CHAR,AX ;Subtract the deleted line
MOV SI,CURSOR ;Get new cursor location
MOV DI,SI
ADD SI,AX ;SI points to end of file
SUB CX,SI ;Length of remaining file
JCXZ NO_DEL_L
REP MOVSB ;Shift remainder of file up
NO_DEL_L:
MOV DX,CUR_POSN ;Get cursor row/column
MOV SI,CURSOR ;Get cursor offset
CALL LOCATE ;Adjust the screen position
MOV DIRTY_BITS,1 ;Redraw the screen
RET
DEL_L ENDP
;-----------------------------------------------------------------------
; This undeletes a line by copying it from the line buffer into the file
;-----------------------------------------------------------------------
UDEL_L PROC NEAR
CMP LINE_FLAG,0 ;Is this an end of line only?
JE UDEL_EOL ;If yes, don't home the cursor
CALL HOME ;Move cursor to home
UDEL_EOL:
MOV AX,LINE_LENGTH ;Length of deleted line
MOV SI,OFFSET LINE_BUFFER
JMP INSERT_STRING
UDEL_L ENDP
;-----------------------------------------------------------------------
; These routines move the cursor left and right.
;-----------------------------------------------------------------------
LEFT PROC NEAR
CMP CURSOR,0 ;At start of file?
JZ LR_NO_CHANGE ;Then can't move left
MOV DX,CUR_POSN
OR DL,DL ;At first column?
JZ MOVE_UP ;If yes, then move up one
DEC CURSOR ;Shift the cursor offset
LR_RETURN:
CALL CURSOR_COL ;Compute column for cursor
MOV SAVE_COLUMN,DL ;Save the cursor column
LR_NO_CHANGE:
MOV UNDO_LENGTH,0
RET
MOVE_UP:
CALL UP ;Move up to next row
JMP SHORT ENDD ;And move to end of line
LEFT ENDP
RIGHT PROC NEAR
MOV SI,CURSOR
CMP SI,LAST_CHAR ;At end of file?
JE LR_NO_CHANGE ;If yes, then can't move
CMP BYTE PTR [SI],CR;If CR
JNE INC_RIGHT ;If yes, then test LF
INC SI
CMP SI,LAST_CHAR ;At end of file?
DEC SI
JE INC_RIGHT ;If yes, then increment
CMP BYTE PTR [SI+1],LF;If LF
JE NEXT_LINE ;If yes, then move to next line
INC_RIGHT:
INC CURSOR ;Advance the cursor
JMP LR_RETURN
NEXT_LINE:
CALL HOME ;Move to start of line
JMP DOWN ;And move down one row
RIGHT ENDP
;-----------------------------------------------------------------------
; This moves the cursor to the start of the current line.
;-----------------------------------------------------------------------
HOME PROC NEAR
CALL FIND_START ;Find start of line
MOV CURSOR,SI ;Save the new cursor
MOV SAVE_COLUMN,0 ;Save the cursor column
MOV BYTE PTR CUR_POSN,0 ;Store column number
RET
HOME ENDP
;-----------------------------------------------------------------------
; This moves the cursor to the end of the current line
;-----------------------------------------------------------------------
ENDD PROC NEAR
MOV SI,CURSOR
CALL FIND_EOL ;Find end of this line
MOV CURSOR,SI ;Store the new cursor
CALL CURSOR_COL ;Compute the correct column
MOV SAVE_COLUMN,DL ;Save the cursor column
RET
ENDD ENDP
;-----------------------------------------------------------------------
; This moves the cursor up one row. If the cursor is at the first row,
; the screen is scrolled down.
;-----------------------------------------------------------------------
UP PROC NEAR
MOV UNDO_LENGTH,0
MOV DX,CUR_POSN
MOV SI,CURSOR
OR DH,DH ;At top row already?
JZ SCREEN_DN ;If yes, then scroll down
DEC DH ;Move cursor up one row
CALL FIND_CR ;Find the beginning of this row
MOV CURSOR,SI
CALL FIND_START ;Find start of this row
MOV CURSOR,SI
CALL SHIFT_RIGHT ;Skip over to current column
AT_TOP:
RET
SCREEN_DN:
MOV SI,TOP_OF_SCREEN
OR SI,SI ;At start of file?
JZ AT_TOP ;If at top, then do nothing
CALL FIND_PREVIOUS ;Find the preceeding line
MOV TOP_OF_SCREEN,SI;Save new top of screen
MOV SI,CURSOR
CALL FIND_PREVIOUS ;Find the preceeding line
MOV CURSOR,SI ;This is the new cursor
SHIFT_RET:
MOV DIRTY_BITS,1 ;Need to redraw screen
MOV SI,CURSOR
MOV DX,CUR_POSN
JMP SHIFT_RIGHT ;Move cursor to current column
UP ENDP
;-----------------------------------------------------------------------
; This moves the cursor down one row. When the last row is reached,
; the screen is shifted up one row.
;-----------------------------------------------------------------------
DOWN PROC NEAR
MOV UNDO_LENGTH,0
MOV DX,CUR_POSN
CMP DH,ROWS ;At bottom row already?
MOV SI,CURSOR ;Get position in file
JE SCREEN_UP ;If at bottom, then scroll up
CALL FIND_NEXT ;Find the start of next line
JC DOWN_RET ;If no more lines, then return
MOV CURSOR,SI
INC DH ;Advance cursor to next row
CALL SHIFT_RIGHT ;Move cursor to current column
DOWN_RET:
RET
SCREEN_UP:
CMP SI,LAST_CHAR ;Get cursor offset
JE DOWN_RET
CALL FIND_START ;Find the start of this line
MOV CURSOR,SI ;This is the new cursor
CALL FIND_NEXT ;Find the offset of next line
JC SHIFT_RET ;If no more lines then return
MOV CURSOR,SI ;This is the new cursor
MOV SI,TOP_OF_SCREEN;Get the start of the top row
CALL FIND_NEXT ;And find the next line
MOV TOP_OF_SCREEN,SI;Store the new top of screen
JMP SHIFT_RET
DOWN ENDP
;-----------------------------------------------------------------------
; These two routines move the screen one page at a time by calling the
; UP and DOWN procedures.
;-----------------------------------------------------------------------
PGDN PROC NEAR
MOV PAGE_PROC,OFFSET DOWN
PAGE_UP_DN:
MOV CL,ROWS ;Get length of the screen
SUB CL,5 ;Don't page a full screen
XOR CH,CH ;Make it a word
PAGE_LOOP:
PUSH CX
CALL PAGE_PROC ;Move the cursor down
POP CX
LOOP PAGE_LOOP ;Loop for one page length
RET
PGDN ENDP
PGUP PROC NEAR
MOV PAGE_PROC,OFFSET UP
JMP PAGE_UP_DN
PGUP ENDP
;-----------------------------------------------------------------------
; This toggles the insert/overstrike mode.
;-----------------------------------------------------------------------
INSERT PROC NEAR
NOT INSERT_MODE ;Toggle the switch
JMP REDO_PROMPT ;Redraw the insert status
INSERT ENDP
;-----------------------------------------------------------------------
; This deletes the character at the cursor by shifting the remaining
; characters forward.
;-----------------------------------------------------------------------
DEL_CHAR PROC NEAR
MOV CX,LAST_CHAR
MOV SI,CURSOR
MOV DI,SI
CMP SI,CX ;Are we at end of file?
JAE NO_DEL ;If yes, then don't delete
LODSB
CALL SAVE_CHAR ;Save it for UNDO function
CMP SI,LAST_CHAR ;at EOF?
JNB MOVE_DOWN
CMP AL,CR ;Is it a CR?
JNE MOVE_DOWN
LODSB
CMP AL,LF ;Is it a LF?
JE DEL_LF
DEC SI ;step back
JMP SHORT MOVE_DOWN
DEL_LF:
CALL SAVE_CHAR ;Save it for UNDO function
CMP WRAP_FLAG,0 ;word wrap on?
JE CR_LF_DEL ;if not then skip
MOV AX,[SI]
CMP AX,LF*256 +CR; Followed by a blank line?
JE CR_LF_DEL
DEC SI ;step back passed EOL
DEC SI
CALL JOIN
JMP SHORT EOL_DEL
MOVE_DOWN:
SUB CX,SI ;Calculate no. of chars to move
MOV AX,CX
ADD AX,DI
MOV LAST_CHAR,AX ;Adjust file length
REP MOVSB ;Move file down one notch
OR DIRTY_BITS,4 ;Current line is dirty
NO_DEL:
RET
CR_LF_DEL:
SUB CX,SI ;Calculate no. of chars to move
MOV AX,CX
ADD AX,DI
MOV LAST_CHAR,AX ;Adjust file length
REP MOVSB ;Move file down one notch
EOL_DEL:
OR DIRTY_BITS,2 ;bottom of screen is dirty
MOV DX,CUR_POSN
MOV SAVE_COLUMN,DL ;Save the cursor column
RET
DEL_CHAR ENDP
;-----------------------------------------------------------------------
; This toggles the mark state and resets the paste buffer pointers.
;-----------------------------------------------------------------------
MARK PROC NEAR
XOR AX,AX
NOT MARK_MODE ;Toggle the mode flag
CMP MARK_MODE,AL ;Turning mode ON?
JNE MARK_ON
MOV DIRTY_BITS,1 ;Need to redraw the screen
MOV MARK_START,0FFFFH
JMP SHORT MARK_RET
MARK_ON:
MOV AX,CURSOR ;Get the cursor offset
MOV MARK_START,AX ;Start of marked range
MARK_RET:
MOV MARK_END, AX ;End of marked range
MOV MARK_HOME, AX ;Center of marked range
RET
MARK ENDP
;-----------------------------------------------------------------------
; This removes the marked text and places it in the paste buffer
;-----------------------------------------------------------------------
CUT PROC NEAR
CMP MARK_MODE,0 ;Is the mark mode on?
JE NO_MARK ;If not, then do nothing
MOV CX,MARK_END ;Get end of mark region
MOV SI,MARK_START ;Get start of mark region
SUB CX,SI ;Number of bytes selected
MOV PASTE_SIZE,CX
JCXZ NO_MARK
XOR DI,DI ;Point to paste bufferf
PUSH CX
PUSH ES
MOV ES,PASTE_SEG ;Get the paste segment
REP MOVSB ;Put deleted text in buffer
POP ES
POP AX
MOV CX,LAST_CHAR
SUB LAST_CHAR,AX ;Shorten the file this much
MOV DI,MARK_START
MOV SI,MARK_END
SUB CX,SI
JCXZ NO_DELETE
REP MOVSB ;Shorten the file
NO_DELETE:
MOV DX,CUR_POSN
MOV SI,MARK_START
CALL LOCATE ;Adjust the screen position
CALL MARK ;This turns off select
NO_MARK:
RET
CUT ENDP
;-----------------------------------------------------------------------
; This copies the paste buffer into the file at the cursor location
;-----------------------------------------------------------------------
PASTE PROC NEAR
MOV AX,PASTE_SIZE ;Number of characters in buffer
OR AX,AX ;Any there?
JZ NO_PASTE ;If not, nothing to paste
PUSH AX
CALL OPEN_SPACE ;Make room for new characters
POP CX
JC NO_PASTE ;If no room, just exit
XOR SI,SI ;Point to paste buffer
MOV DI,CURSOR ;destination is cursor position
PUSH DS
MOV DS,PASTE_SEG ;Segment of paste buffer
REP MOVSB ;Copy in the new characters
POP DS
MOV CURSOR,DI ;Cursor moved to end of insert
PASTE_END:
MOV SI,CURSOR
MOV DX,CUR_POSN ;Get current cursor row
CALL LOCATE ;Adjust the screen position
MOV DIRTY_BITS,1 ;Redraw the screen
NO_PASTE:
RET
PASTE ENDP
;-----------------------------------------------------------------------
; This copies the paste buffer into the file at the cursor location with
; a quote character at start of each line
;-----------------------------------------------------------------------
QUOTE PROC NEAR
MOV AX,PASTE_SIZE ;Number of characters in buffer
OR AX,AX ;Any there?
JZ NO_PASTE ;If not, nothing to paste
XOR SI,SI ;top of buffer
PASTE_LOOP:
PUSH SI
PUSH DS
PUSH ES
MOV DS,PASTE_SEG ;change to paste segment
MOV ES,PASTE_SEG
PUSH LAST_CHAR
MOV AX,PASTE_SIZE
MOV LAST_CHAR,AX
CALL FIND_NEXT ;find the start of the next line
POP LAST_CHAR
POP ES
POP DS ;restore file segment
MOV AX,SI
POP SI
SUB AX,SI ;No of chars in line
JZ PASTE_END ;no more to move
PUSH AX
INC AX ;plus space for quote character
PUSH SI
CALL OPEN_SPACE ;make room
POP SI
POP CX
JC PASTE_END ;exit if no more room
PUSH DS
MOV DS,PASTE_SEG
MOV DI,CURSOR
MOV AL,QUOTE_CHAR ;First insert quote character
STOSB
REP MOVSB ;then insert next line
POP DS
MOV CURSOR,DI ;set cursor to after insertion
JMP SHORT PASTE_LOOP
QUOTE ENDP
;-----------------------------------------------------------------------
; This prints the marked text. If printer fails, it is canceled.
;-----------------------------------------------------------------------
PRINT PROC NEAR
CMP MARK_MODE,0 ;Is mark mode on?
JE PRINT_ALL ;If not, print whole file
MOV CX,MARK_END ;End of marked region
MOV SI,MARK_START ;Start of marked region
SUB CX,SI ;Number of bytes selected
JCXZ PRINT_DONE ;If nothing to print, return
JMP PRINT_TEST
PRINT_ALL:
MOV CX,LAST_CHAR ;End of File
JCXZ PRINT_DONE ;If nothing to print, return
PUSH CX
MOV SI,OFFSET PRINT_CONFIRM
CALL ASK_STRING ;get confirmation
POP CX
JNE PRINT_DONE ;Exit if no
XOR SI,SI ;Start of File
PRINT_TEST:
MOV AH,2
XOR DX,DX ;Select printer 0
INT 17H ;Get printer status
TEST AH,10000000B ;Is busy bit set?
JZ PRINT_DONE
TEST AH,00100000B ;Is printer out of paper?
JNZ PRINT_DONE
PRINT_LOOP:
LODSB
XOR AH,AH
INT 17H ;Print the character
ROR AH,1 ;Check time out bit
JC PRINT_DONE ;If set, quit printing
LOOP PRINT_LOOP
MOV AL,CR
XOR AH,0
INT 17H ;Finish with a CR
PRINT_DONE:
CMP MARK_MODE,0 ;Is mark mode on?
JE PRINT_FF ;If not then send a FF
CALL MARK ;Turn off the mark state
PRINT_RET:
RET
PRINT_FF:
MOV AL,FF ;Print a FF
XOR AH,AH
INT 17H
RET
PRINT ENDP
;-----------------------------------------------------------------------
; This subroutine reads a named file into the paste buffer and then pastes
; the contents of the paste buffer into the file being edited at the current
; cursor position.
;-----------------------------------------------------------------------
INSERT_FILE PROC NEAR
ASSUME DS:CSEG
PUSH DS
PUSH CS
POP DS
MOV AX,PASTE_SIZE ;Check for empty paste buffer
OR AX,AX
JZ GET_IN_FILENAME ;Empty, then get on with it
MOV SI, OFFSET DEL_PASTE_MESS
CALL ASK_STRING ;Ask if OK to overwrite
JNE IN_ERROR_EXIT ;If no then do nothing
GET_IN_FILENAME:
MOV DI,OFFSET IN_FILE_MESS ;Input prompt
MOV SI,OFFSET IN_FILENAME ;Buffer to receive filename
MOV CX,BUFF_SIZE
CALL READ_STRING ;prompt for filename
MOV AL,IN_FILENAME
OR AL,AL ;Is it zero:
JZ IN_ERROR_EXIT ;Empty string - do nothing
MOV SI,OFFSET IN_FILENAME
IN_DEL_SPACES: ;Remove leading spaces
LODSB ;Get next character
CMP AL," " ;is it a space?
JNE IN_READ_FILE ;Non-space found then filename found
LOOP IN_DEL_SPACES
IN_READ_FILE:
DEC SI ;Point again to first letter
MOV DX,SI
MOV AX,3D00H ;Command code to open file
INT 21H
JC IN_BAD_FILE ;Error exit
PUSH DS
MOV DS,PASTE_SEG ;DS points to paste buffer
XOR DX,DX
MOV BX,AX ;Put file handle into BX
MOV AH,3FH ;Read Command
MOV CX,0FFFEH ;Set maximum at just under 64K
INT 21H
POP DS ;Restore DS
JC IN_READ_ERROR ;Error exit
PUSH BX
MOV PASTE_SIZE,AX ;No. of bytes read in
CMP CX,AX ;All of file read in?
JNE IN_PASTE
MOV SI,OFFSET TRUNCATE_MESS
CALL ERROR_MESG
IN_PASTE:
POP BX
MOV AH,3EH ;Close the file
INT 21H
POP DS
ASSUME DS:NOTHING
CMP QUOTE_FLAG,0 ;Need to quote file?
JZ PASTE_FILE
CALL QUOTE
JMP SHORT INSERT_COMPLETE
PASTE_FILE:
CALL PASTE ;Insert read in file contents
JMP SHORT INSERT_COMPLETE
IN_READ_ERROR:
MOV SI,OFFSET IN_READ_ERR
CALL ERROR_MESG
JMP SHORT IN_ERROR_EXIT
IN_BAD_FILE:
MOV SI,OFFSET NO_FILE_MESS
CALL ERROR_MESG
IN_ERROR_EXIT:
POP DS
INSERT_COMPLETE:
MOV QUOTE_FLAG,0
CALL REDO_PROMPT
RET ;Exit
INSERT_FILE ENDP
;-----------------------------------------------------------------------
; This command restores any characters which have recently been deleted.
;-----------------------------------------------------------------------
UNDO PROC NEAR
XOR AX,AX
XCHG AX,UNDO_LENGTH ;Get buffer length
MOV SI,OFFSET UNDO_BUFFER
JMP INSERT_STRING
UNDO ENDP
;-----------------------------------------------------------------------
; This inserts AX characters from CS:SI into the file.
;-----------------------------------------------------------------------
INSERT_STRING PROC NEAR
PUSH SI ;Save string buffer
MOV SI,CURSOR ;Get cursor offset
PUSH AX ;Save length of string
PUSH SI
CALL OPEN_SPACE ;Make space to insert string
POP DI ;Get back cursor position
POP CX ;Get back string length
POP SI ;Get back string buffer
JC NO_SPACE ;If no space available, exit
PUSH DS
PUSH CS
POP DS
ASSUME DS:CSEG
REP MOVSB ;Copy the characters in
MOV SI,CURSOR ;Get the new cursor offset
MOV DX,CUR_POSN ;Also get the current row
MOV DIRTY_BITS,1 ;And redraw the screen
POP DS
ASSUME DS:NOTHING
CALL LOCATE ;Adjust the screen position
NO_SPACE:
RET
INSERT_STRING ENDP
;-----------------------------------------------------------------------
; This adds a character to the undo buffer.
;-----------------------------------------------------------------------
SAVE_CHAR PROC NEAR
MOV BX,UNDO_LENGTH
OR BH,BH ;Is buffer filled?
JNZ NO_SAVE
INC UNDO_LENGTH
MOV BYTE PTR CS:UNDO_BUFFER[BX],AL
NO_SAVE:
RET
SAVE_CHAR ENDP
;-----------------------------------------------------------------------
; This subroutine draws a double line across the screen AX = corner chars
;------------------------------------------------------------------------
LINE_DRAW PROC NEAR
XOR DL,DL ;And column 0
PUSH AX
CALL POSITION ;Convert to screen offset
POP AX ;AL contains left hand corner
PUSH AX
CALL WRITE_INVERSE
MOV CX,COLUMNS ;screen width
DEC CX
DEC CX
DRAW_LOOP:
MOV AL,"═" ;output line character
CALL WRITE_INVERSE
LOOP DRAW_LOOP
POP AX
MOV AL,AH
CALL WRITE_INVERSE
RET
LINE_DRAW ENDP
;-----------------------------------------------------------------------
; This Subrouting displays a HELP screen
;-----------------------------------------------------------------------
HELP PROC NEAR
PUSH DS
PUSH CS
POP DS
ASSUME DS:CSEG
MOV DH,HELP_ROW ;Put prompt at start of help
MOV AL,"╔" ;Output top left hand corner
MOV AH,"╗" ;output top right hand corner
PUSH DX
CALL LINE_DRAW
POP DX
MOV SI,OFFSET HELP_TEXT
LINE_LOOP:
INC DH ;next row
XOR DL,DL ;column 0
PUSH DX
CALL POSITION ;goto start of next row
MOV AL,"║" ;output vertical line
CALL WRITE_INVERSE
POP DX
INC DL
PUSH DX
CALL ERASE_EOL ;clear reset of line
POP DX
MOV CX,HELP_COLS ;Four columns on screen
HELP_LINE_LOOP:
MOV DI,HELP_COLS
SUB DI,CX ;Form index for col start array
MOV DL,HELP_OFFSETS[DI]
PUSH CX
PUSH DX
CALL POSITION ;goto column 4
HELP_TEXT_LOOP:
LODSB ;get first character of function name
OR AL,AL ;zero is end of text
JZ END_HELP_TEXT
CMP AL,-1 ;Test for End of text
JE HELP_END
CALL WRITE_NORMAL
JMP SHORT HELP_TEXT_LOOP
END_HELP_TEXT:
POP DX
POP CX
LOOP HELP_LINE_LOOP
MOV DL,COLUMNSB
DEC DL
PUSH DX
CALL POSITION ;goto last column
MOV AL,"║" ;output vertical line
CALL WRITE_INVERSE
POP DX
JMP LINE_LOOP
HELP_END:
POP DX
POP CX
MOV DL,26 ;column 26
MOV SI,OFFSET CONTINUE_MESS
PUSH DX
CALL TTY_STRING
POP DX
MOV DL,COLUMNSB
DEC DL
PUSH DX
CALL POSITION ;goto column 80
MOV AL,"║" ;output vertical line
CALL WRITE_INVERSE
POP DX
INC DH ;next row
MOV AL,"╚" ;Output bottom left hand corner
MOV AH,"╝" ;output bottom right hand corner
CALL LINE_DRAW
MOV AH,KEYREAD_CODE
INT 16H ;wait for key press
POP DS
ASSUME DS:NOTHING
CALL REDO_PROMPT
MOV DIRTY_BITS,1 ;redraw screen
RET
HELP ENDP
;-----------------------------------------------------------------------
; This prompts for a verify keystroke then exits without saving the file
;-----------------------------------------------------------------------
ABORT PROC NEAR
MOV SI, OFFSET ABORT_MESS
CALL ASK_STRING ;Prompt to confirm abort
JE FINISHED ;If result is yes then exit
RET ;Otherwise continue
FINISHED:
ASSUME DS:CSEG
PUSH CS
POP DS
MOV DH,ROWS ;Move to last row on screen
XOR DL,DL ;And column zero
CALL SET_CURSOR
INC DH
MOV AL,SAVE_ATR ;Get original screen attribute
MOV ORGATR,AL ;Restore original screen attribute
CALL ERASE_EOL ;Erase the last row
MOV DX,OFFSET COPYRIGHT
MOV AH,9
INT 21H ;Output copyright message
EXIT_TO_DOS:
MOV AX,4C00H
INT 21H
ABORT ENDP
;-----------------------------------------------------------------------
;sAVES FILE AND THEN EXITS IF ok
;-----------------------------------------------------------------------
EXIT PROC NEAR
IFNDEF MAIL
CALL SAVE_FILE
ELSE
CALL TEST_FILENAME
ENDIF
JC FINISHED
RET
EXIT ENDP
;-----------------------------------------------------------------------
; This prompts for a filename then writes the file. The original file
; is renamed to filename.BAK. If an invalid filename is entered, the
; speaker is beeped. Carry set if OK to exit
;-----------------------------------------------------------------------
SAVE_FILE PROC
IFDEF MAIL
PUSH DS
PUSH ES
MOV AX,CS
MOV DS,AX
MOV ES,AX
MOV SI,NAME_POINTER
MOV DI,OFFSET SAVE_FILENAME
SAVE_LOOP1: ;Save current filename
LODSB
STOSB
CMP AL,0
JE SAVE_LOOP1_DONE
JMP SHORT SAVE_LOOP1
SAVE_LOOP1_DONE:
POP ES
POP DS
CALL SAVEAS
PUSH DS
PUSH ES
MOV AX,CS
MOV DS,AX
MOV ES,AX
MOV DI,NAME_POINTER
MOV SI,OFFSET SAVE_FILENAME
SAVE_LOOP2: ;Restore current filename
LODSB
STOSB
CMP AL,0
JE SAVE_LOOP2_DONE
JMP SHORT SAVE_LOOP2
SAVE_LOOP2_DONE:
POP ES
POP DS
RET
SAVEAS:
ENDIF
ASSUME DS:NOTHING
MOV DI,OFFSET SAVE_MESS
MOV SI,NAME_POINTER
MOV CX,81H+BUFF_SIZE
SUB CX,SI ;buffer space available
CALL READ_STRING
JC TEST_FILENAME
CALL REDO_PROMPT
CLC
RET
TEST_FILENAME:
PUSH DS
PUSH ES
MOV AX,CS
MOV DS,AX
MOV ES,AX
ASSUME DS:CSEG, ES:CSEG
MOV DX,NAME_POINTER ;Point to the filename
MOV AX,4300H ;Get the files attribute
INT 21H
JNC NAME_OK ;If no error, filename is OK
CMP AX,3 ;Was it path not found error?
JE BAD_NAME ;If yes, filename was bad
NAME_OK:
MOV SI,OFFSET DOT_$$$ ;Point to the ".$$$"
MOV DI,OFFSET NAME_DOT_$$$
CALL CHG_EXTENSION ;Add the new extension
MOV DX,OFFSET NAME_DOT_$$$ ;Point to the temp filename
MOV AH,3CH ;Function to create file
MOV CX,0020H ;Attribute for new file
INT 21H ;Try to create the file
JNC NAME_WAS_OK ;Continue if name was OK
BAD_NAME:
MOV AX,0E07H ;Write a bell character
INT 10H ;BIOS tty service
CLC
JMP SAVE_ERROR_EXIT ;Get another letter
WRITE_ERROR:
MOV AH,3EH ;Close the file
INT 21H
JMP BAD_NAME ;Filename must be bad
NAME_WAS_OK:
XOR DX,DX ;This is the file buffer
MOV CX,LAST_CHAR ;Number of chars in file
MOV DI,CX
MOV BX,AX ;This is the handle
JCXZ EXIT_ZERO ;Empty File?
MOV AH,40H ;Write to the file
POP DS ;Recover buffer segment
INT 21H ;Write the buffer contents
PUSH DS
JC WRITE_ERROR ;Exit on a write error
CMP AX,CX ;Was entire file written?
JNE WRITE_ERROR ;If not, exit
EXIT_ZERO:
PUSH CS
POP DS ;Get the code segment
MOV AH,3EH
INT 21H ;Close the temp file
MOV SI,OFFSET DOT_BAK ;Point to the ".BAK"
MOV DI,OFFSET NAME_DOT_BAK
CALL CHG_EXTENSION ;Make the backup filename
MOV DX,OFFSET NAME_DOT_BAK ;Point to the backup name
MOV AH,41H
INT 21H ;Delete existing backup file
MOV DI,OFFSET NAME_DOT_BAK
MOV DX,NAME_POINTER
MOV AH,56H
INT 21H
MOV DI,NAME_POINTER ;Point to new filename
MOV DX,OFFSET NAME_DOT_$$$ ;Point to temporary file
MOV AH,56H ;Rename temp to new file
INT 21H ;DOS function to rename
STC
SAVE_ERROR_EXIT:
POP ES ;Restore the stack
POP DS
RET
SAVE_FILE ENDP
;----------------------------------------------------------------------
; This subroutine reads a string of up to CX (<=BUFF_SIZE) characters from the keyboard
; DI points to prompt, SI to buffer
;----------------------------------------------------------------------
READ_STRING PROC NEAR
PUSH DS
PUSH ES
MOV AX,CS
MOV DS,AX
MOV ES,AX
ASSUME DS:CSEG, ES:CSEG
MOV BX,SI ;save start of buffer
PUSH DI ;save prompt pointer
PUSH CX
MOV DI,OFFSET SAVE_NAME_BUFF
REP MOVSB ;save current buffer contents
POP CX
POP SI ;SI is now prompt pointer
NEXT_LETTER:
MOV DH,ROWS
INC DH ;Last row on the screen
XOR DL,DL ;First column
PUSH BX
PUSH CX
PUSH SI
PUSH BX
CALL TTY_STRING ;Display a prompt
POP SI ;get address of input buffer
CALL TTY_STRING ;Display the filename
DEC SI
MOV DI,SI ;DI points to EOB
POP SI
POP CX
POP BX
MOV AH,KEYREAD_CODE ;Read the next key
INT 16H
OR AL,AL ;Is it a real character?
JZ SPECIAL_KEY
CMP AL,224 ;Extended Key?
JE SPECIAL_KEY
CMP AL,ESC ;Is it escape?
JNE NOT_ESCAPE
MOV DI,BX
MOV SI,OFFSET SAVE_NAME_BUFF
REP MOVSB ;restore original contents
CLC
JMP SHORT READS_END ;Then exit
SPECIAL_KEY:
CMP AH,DEL ;Is it a Delete char?
JNE NEXT_LETTER ;otherwise ignore
MOV BYTE PTR [BX],0 ;Clear buffer
JMP SHORT NEXT_LETTER
NOT_ESCAPE:
CMP AL,CR ;Is it CR?
JE GOT_NAME
CMP AL,BS ;Is it a backspace?
JNE NORMAL_LETTER
CMP BX,DI ;At first letter?
JNB NEXT_LETTER ;If yes, dont erase it
DEC DI
MOV BYTE PTR [DI],0
JMP SHORT NEXT_LETTER
NORMAL_LETTER:
MOV DX,DI
SUB DX,BX
CMP DX,CX ;Too many letters?
JA NEXT_LETTER ;If yes, ignore them
XOR AH,AH
STOSW ;Store the new letter
DEC DI
JMP NEXT_LETTER ;Read another keystroke
GOT_NAME:
STC
READS_END:
PUSHF
CALL REDO_PROMPT
POPF
POP ES
POP DS
RET
READ_STRING ENDP
;-----------------------------------------------------------------------
; This subroutine displays a character by writing directly
; to the screen buffer. To avoid screen noise (snow) on the color
; card, the horizontal retrace has to be monitored.
;-----------------------------------------------------------------------
WRITE_INVERSE PROC NEAR
ASSUME DS:FILE_SEG, ES:FILE_SEG
MOV BH,INVATR
JMP SHORT WRITE_SCREEN
WRITE_NORMAL:
MOV BH,ORGATR ;Attribute for normal video
JMP SHORT WRITE_SCREEN
WRITE_FIND:
MOV BH,SRCHATR ;Attribute for find string
WRITE_SCREEN:
MOV BL,AL ;Save the character
PUSH ES
MOV DX,STATUS_REG ;Retrieve status register
MOV ES,VIDEO_SEG ;Get segment of video buffer
HWAIT:
IN AL,DX ;Get video status
ROR AL,1 ;Look at horizontal retrace
JNC HWAIT ;Wait for retrace
WRITE_IT:
MOV AX,BX ;Get the character/attribute
STOSW ;Write the character
POP ES
RET
WRITE_INVERSE ENDP
;-----------------------------------------------------------------------
; This moves the cursor to the row/column in DX.
;-----------------------------------------------------------------------
SET_CURSOR PROC NEAR
XOR BH,BH ;Were using page zero
MOV AH,2 ;BIOS set cursor function
INT 10H
RET
SET_CURSOR ENDP
;-----------------------------------------------------------------------
; This computes the video buffer offset for the row/column in DX
;----------------------------------------------------------------------
POSITION PROC NEAR
MOV AX,COLUMNS ;Take columns per row
MUL DH ;Times row number
XOR DH,DH
ADD AX,DX ;Add in the column number
SHL AX,1 ;Times 2 for offset
MOV DI,AX ;Return result in DI
RET
POSITION ENDP
;-----------------------------------------------------------------------
; This erases from the location in DX to the right edge of the screen
;-----------------------------------------------------------------------
ERASE_EOL PROC NEAR
PUSH DX
CALL POSITION ;Find screen offset
MOV CX,COLUMNS ;Get screen size
SUB CL,DL ;Subtract current position
JCXZ NO_CLEAR
ERASE_LOOP:
MOV AL," " ;Write blanks to erase
CALL WRITE_NORMAL ;Display it
LOOP ERASE_LOOP
NO_CLEAR: POP DX
RET
ERASE_EOL ENDP
;-----------------------------------------------------------------------
; This outputs a message (pointed to by SI) and waits for a Y/N
; response. If 'N' the prompt line is restored and a ZF is cleared
; is returned. Otherwise ZF is set on return
;-----------------------------------------------------------------------
ASK_STRING PROC NEAR
ASSUME DS:CSEG
PUSH DS
PUSH CS
POP DS
MOV DH,ROWS ;Last row on display
INC DH ;Bottom row of screen
XOR DL,DL ;First column
MOV DH,ROWS ;Move to last row on screen
INC DH
XOR DL,DL ;And column zero
CALL TTY_STRING
MOV AH,KEYREAD_CODE
INT 16H ;Get character from keyboard
CMP AL,"Y" ; Abort only if confirmed
JE ASK_RETURNS_YES
CMP AL,"y"
ASK_RETURNS_YES:
PUSHF ;Save the result status
CALL REDO_PROMPT
POPF
POP DS
RET
ASK_STRING ENDP
;-----------------------------------------------------------------------
; This outputs a message (pointed to by SI), adds a prompt
; and then waits for a response. The prompt line is then restored.
;-----------------------------------------------------------------------
ERROR_MESG PROC NEAR
ASSUME DS:CSEG
PUSH DS
PUSH CS
POP DS
MOV DH,ROWS ;Last row on display
INC DH ;Bottom row of screen
XOR DL,DL ;First column
MOV DH,ROWS ;Move to last row on screen
INC DH
XOR DL,DL ;And column zero
CALL TTY_STRING ;Output error message
MOV SI,OFFSET PAD_MESS
CALL TTY_STRING
MOV SI,OFFSET CONTINUE_MESS
CALL TTY_STRING
MOV AH,KEYREAD_CODE
INT 16H ;Get character from keyboard
CALL REDO_PROMPT
POP DS
RET
ERROR_MESG ENDP
;-----------------------------------------------------------------------
; This displays the function key prompt, word wrap and insert mode state
;-----------------------------------------------------------------------
REDO_PROMPT PROC NEAR
ASSUME DS:NOTHING, ES:NOTHING
PUSH DS
PUSH CS
POP DS
ASSUME DS:CSEG
MOV DH,ROWS ;Put prompt at last row
INC DH
XOR DL,DL ;And column 0
CALL POSITION ;Convert to screen offset
MOV SI,OFFSET PROMPT_STRING
KEY_LOOP:
MOV AL,"F" ;Display an "F"
CALL WRITE_NORMAL
LODSB
OR AL,AL ;Last key in prompt?
JZ PROMPT_DONE
CALL WRITE_NORMAL
CMP BYTE PTR CS:[SI],"0" ;Is it F10?
JNE TEXT_LOOP
LODSB
CALL WRITE_NORMAL
TEXT_LOOP:
LODSB
OR AL,AL ;Last letter in word?
JNZ WRITE_CHAR
MOV AL," " ;Display a space
CALL WRITE_NORMAL
JMP KEY_LOOP
WRITE_CHAR:
CALL WRITE_INVERSE ;Display the letter
JMP TEXT_LOOP ;Do the next letter
PROMPT_DONE:
MOV DH,ROWS
INC DH ;Get to last row on screen
MOV DL,PROMPT_LENGTH + 9
CALL ERASE_EOL ;Erase to the end of this row
DEC DI ;Backup two character positions
DEC DI
DEC DI
DEC DI
MOV AL," " ;indicates no word wrap
CMP WRAP_FLAG,0 ;test word wrap flag
JE NO_WRAP
MOV AL,"W" ;indicate word wrap on
NO_WRAP:
CALL WRITE_NORMAL
MOV AL,"O" ;Write an "O"
CMP INSERT_MODE,0 ;In insert mode?
JE OVERSTRIKE
MOV AL,"I" ;Write an "I"
OVERSTRIKE:
CALL WRITE_NORMAL
POP DS
RET
REDO_PROMPT ENDP
;-----------------------------------------------------------------------
; This displays the file buffer on the screen.
;-----------------------------------------------------------------------
DISPLAY_SCREEN PROC NEAR
ASSUME DS:FILE_SEG, ES:FILE_SEG
MOV SI,TOP_OF_SCREEN;Point to first char on screen
XOR DH,DH ;Start at first row
JMP SHORT NEXT_ROW
DISPLAY_BOTTOM: ;This redraws the bottom only
CALL FIND_START ;Find first character on this row
MOV DX,CUR_POSN ;Get current cursor row
NEXT_ROW:
PUSH DX
CALL DISPLAY_LINE ;Display a line
POP DX
INC DH ;Move to the next row
CMP DH,ROWS ;At end of screen yet?
JBE NEXT_ROW ;Do all the rows
RET
DISPLAY_SCREEN ENDP
;-----------------------------------------------------------------------
; This subroutine displays a single line to the screen. DH holds the
; row number, SI has the offset into the file buffer. Tabs are expanded.
; Adjustment is made for side shift.
;-----------------------------------------------------------------------
DISPLAY_CURRENT PROC NEAR
CALL FIND_START
MOV DX,CUR_POSN
DISPLAY_LINE:
XOR DL,DL ;Start at column zero
MOV MARGIN_COUNT,DL
MOV CX,DX ;Use CL to count the columns
CALL POSITION ;Compute offset into video
NEXT_CHAR:
CMP SI,LAST_CHAR ;At end of file?
JAE LINE_DONE
LODSB ;Get next character
CMP AL,CR ;Is it a carriage return?
JE FOUND_CR ;Quit when a CR is found
CMP AL,TAB ;Is this a Tab character
JE EXPAND_TAB ;If yes, expand to spaces
DO_PUT:
CALL PUT_CHAR ;Put character onto screen
TAB_DONE:
CMP CL,COLUMNSB ;At right edge of screen?
JB NEXT_CHAR
LN_OVF:
CMP SI,LAST_CHAR ;At end of file?
JAE NOT_BEYOND
CMP BYTE PTR [SI],CR
JNE DO_DIA
INC SI
CMP SI,LAST_CHAR ;At end of file?
JAE NOT_BEYOND
CMP BYTE PTR [SI],LF;Is this the end of the line?
JNE DO_DIA
DEC SI
JMP FIND_NEXT
DO_DIA: DEC DI ;Backup one character
DEC DI
MOV AL,4 ;Show a diamond
CALL WRITE_INVERSE ;In inverse video
NOT_BEYOND:
JMP FIND_NEXT ;Find start of next line
FOUND_CR:
LODSB ;Look at the next character
CMP AL,LF ;Is it a line feed?
JE LINE_DONE
MOV AL,CR
DEC SI
JMP SHORT DO_PUT
LINE_DONE:
MOV DX,CX
JMP ERASE_EOL ;Erase the rest of the line
EXPAND_TAB:
MOV AL," " ;Convert Tabs to spaces
CALL PUT_CHAR
MOV AL,MARGIN_COUNT
ADD AL,CL
TEST AL,00000111B ;At even multiple of eight?
JNZ EXPAND_TAB ;If not keep adding spaces
JMP TAB_DONE
DISPLAY_CURRENT ENDP
;-----------------------------------------------------------------------
; This displays a single character to the screen. If the character is
; marked, it is shown in inverse video. Characters outside the current
; margin are not displayed. Characters left of the margin are skipped.
;-----------------------------------------------------------------------
PUT_CHAR PROC NEAR
MOV BL,MARGIN_COUNT ;Get distance to left margin
CMP BL,LEFT_MARGIN ;Are we inside left margin?
JAE IN_WINDOW ;If yes, show the character
INC BL
MOV MARGIN_COUNT,BL
RET
IN_WINDOW: CMP SRCH_FLG,0
JE CKM
CMP SI,CURSOR
JBE CKM
CMP SI,SRCH_STR_END
JA CKM
CALL WRITE_FIND
JMP SHORT NEXT_COL
CKM:
CMP SI,MARK_START ;Is this character marked?
JBE NOT_MARKED
CMP SI,MARK_END
JA NOT_MARKED
CALL WRITE_INVERSE ;Marked characters shown inverse
JMP SHORT NEXT_COL
NOT_MARKED:
CALL WRITE_NORMAL
NEXT_COL:
INC CL ;Increment the column count
RET
PUT_CHAR ENDP
;-----------------------------------------------------------------------
; This routine adds a character into the file. In insert mode, remaining
; characters are pushed forward. If a CR is inserted, a LF is added also.
;-----------------------------------------------------------------------
INSERT_KEY PROC NEAR
MOV SI,CURSOR
CMP AL,CR ;Was this a carriage return
JNE CK_INS
CMP AH,1CH
JNE CK_INS
JMP NEW_LINE
CK_INS:
MOV SI,CURSOR
CMP INSERT_MODE,0 ;In insert mode?
JNE INSERT_CHAR
CMP SI,LAST_CHAR ;At end of file?
JE INSERT_CHAR
CMP BYTE PTR [SI],CR
INC SI
CMP SI,LAST_CHAR ;At end of file?
DEC SI
JE INSERT_CHAR
CMP BYTE PTR [SI+1],LF;At end of line?
JE INSERT_CHAR
MOV DI,SI
XCHG DS:[SI],AL ;Switch new character for old one
CALL SAVE_CHAR ;Store the old character
JMP SHORT ADVANCE
INSERT_CHAR:
PUSH SI
PUSH AX ;Save the new character
MOV AX,1
CALL OPEN_SPACE ;Make room for it
POP AX ;Get back the new character
POP DI
JC FILE_FULL
STOSB ;Insert character in file buffer
ADVANCE:
OR DIRTY_BITS,4 ;Current line is dirty
PUSH UNDO_LENGTH
CALL RIGHT ;Move cursor to next letter
POP UNDO_LENGTH
CMP WRAP_FLAG,0 ;Word Wrap on?
JE FILE_FULL ;Skip if not
CALL WRAP
FILE_FULL:
RET
NEW_LINE:
PUSH SI
MOV AX,2
CALL OPEN_SPACE ;Make space for CR and LF
POP DI ;Get back old cursor location
JC FILE_FULL
MOV AX,LF*256+CR
STOSW ;Store the CR and LF
CALL DISPLAY_BOTTOM ;Repaint bottom of the screen
CALL HOME ;Cursor to start of line
JMP DOWN ;Move down to the new line
INSERT_KEY ENDP
;-----------------------------------------------------------------------
; This subroutine performs line wrap and paragraph
; reformatting
;-----------------------------------------------------------------------
WRAP PROC NEAR
PUSH CURSOR
CALL FORMAT_LINE
POP SI
JNC END_WRAP ;Skip if no split
PUSH CURSOR
PUSH CUR_POSN
CMP SI,CURSOR ;has cursor been advanced
JB ADV_AGAIN
MOV SI,CURSOR
CALL FIND_NEXT
MOV CURSOR,SI
ADV_AGAIN:
CALL JOIN_LINES
JC ADV_END
CALL FORMAT_LINE
JNC ADV_END
ADV_2:
MOV SI,CURSOR
CALL FIND_EOL
MOV CURSOR,SI
JMP SHORT ADV_AGAIN
ADV_END:
CALL FORMAT_LINE ;last line shorter than screen width?
JC ADV_2
POP CUR_POSN
POP CURSOR
MOV DX,CUR_POSN
MOV SI,CURSOR
CALL LOCATE
END_WRAP:
RET
WRAP ENDP
;-----------------------------------------------------------------------
; This subroutine inserts spaces into the file buffer. On entry AX
; contains the number of spaces to be inserted. On return, CF=1 if
; there was not enough space in the file buffer.
;-----------------------------------------------------------------------
OPEN_SPACE PROC NEAR
MOV CX,LAST_CHAR ;Last character in the file
MOV SI,CX
MOV DI,CX
ADD DI,AX ;Offset for new end of file
JC NO_ROOM ;If no more room, return error
MOV LAST_CHAR,DI ;Save offset of end of file
SUB CX,CURSOR ;Number of characters to shift
DEC DI
DEC SI
STD ;String moves goes forward
REP MOVSB ;Shift the file upward
CLD
CLC
NO_ROOM:
RET
OPEN_SPACE ENDP
;-----------------------------------------------------------------------
; This subroutine adjusts the cursor position ahead to the saved cursor
; column. On entry DH has the cursor row.
;-----------------------------------------------------------------------
SHIFT_RIGHT PROC NEAR
MOV CL,SAVE_COLUMN ;Keep the saved cursor offset
XOR CH,CH
MOV BP,CX ;Keep the saved cursor position
ADD CL,LEFT_MARGIN ;Shift into visable window also
ADC CH,0
XOR DL,DL
MOV CUR_POSN,DX ;Get cursor row/column
JCXZ NO_CHANGE
RIGHT_AGAIN:
PUSH CX
CMP BYTE PTR [SI],CR;At end of line?
JE DONT_MOVE ;If at end, stop moving
CALL RIGHT ;Move right one character
DONT_MOVE:
POP CX
MOV AL,SAVE_COLUMN
XOR AH,AH
CMP AX,CX ;Is cursor still in margin?
JL IN_MARGIN ;If yes, keep moving
MOV DX,CUR_POSN ;Get cursor column again
XOR DH,DH
CMP DX,BP ;At saved cursor position?
JE RIGHT_DONE ;If yes, were done
JA RIGHT_TOO_FAR ;Did we go too far?
IN_MARGIN:
LOOP RIGHT_AGAIN
RIGHT_DONE:
MOV CX,BP
MOV SAVE_COLUMN,CL ;Get back saved cursor position
NO_CHANGE:
RET
RIGHT_TOO_FAR:
CALL LEFT ;Move back left one place
MOV CX,BP
MOV SAVE_COLUMN,CL ;Get back saved cursor position
RET
SHIFT_RIGHT ENDP
;-----------------------------------------------------------------------
; This subroutine skips past the CR and LF at SI. SI returns new offset
;-----------------------------------------------------------------------
SKIP_CR_LF PROC NEAR
CMP SI,LAST_CHAR ;At last char in the file?
JAE NO_SKIP ;If yes, dont skip anything
CMP BYTE PTR [SI],CR;Is first character a CR?
JNE NO_SKIP
INC SI ;Look at next character
CMP SI,LAST_CHAR ;Is it at the end of file?
JAE NO_SKIP ;If yes, dont skip anymore
CMP BYTE PTR [SI],LF;Is next character a line feed?
JNE NO_SKIP ;Skip any line feeds also
INC SI
NO_SKIP:
RET
SKIP_CR_LF ENDP
;-----------------------------------------------------------------------
; This subroutine finds the beginning of the previous line.
;-----------------------------------------------------------------------
FIND_PREVIOUS PROC NEAR
PUSH CURSOR ;Save the cursor location
CALL FIND_CR ;Find start of this line
MOV CURSOR,SI ;Save the new cursor
CALL FIND_START ;Find the start of this line
POP CURSOR ;Get back starting cursor
RET
FIND_PREVIOUS ENDP
;-----------------------------------------------------------------------
; This searches for the previous carriage return. Search starts at SI.
;-----------------------------------------------------------------------
FIND_CR PROC NEAR
PUSH CX
MOV AL,LF ;Look for a carriage return
MOV DI,SI
MOV CX,SI
JCXZ AT_BEGINNING
DEC DI
STD ;Search backwards
LF_PREV:
REPNE SCASB ;Scan for the character
JCXZ LF_END
CMP BYTE PTR [DI],CR
JNE LF_PREV
DEC DI
LF_END:
CLD ;Restore direction flag
INC DI
MOV SI,DI
AT_BEGINNING:
POP CX
RET
FIND_CR ENDP
;-----------------------------------------------------------------------
; This subroutine computes the location of the start of current line.
; Returns SI pointing to the first character of the current line.
;-----------------------------------------------------------------------
FIND_START PROC NEAR
MOV SI,CURSOR ;Get the current cursor
OR SI,SI ;At start of the file?
JZ AT_START ;If yes, were done
CALL FIND_CR ;Find the
CALL SKIP_CR_LF
AT_START:
RET
FIND_START ENDP
;-----------------------------------------------------------------------
; This finds the offset of the start of the next line. The search is
; started at location ES:SI. On return CF=1 of no CR was found.
;-----------------------------------------------------------------------
FIND_NEXT PROC NEAR
PUSH CX
CALL FIND_EOL ;Find the end of this line
JC AT_NEXT ;If at end of file, return
CALL SKIP_CR_LF ;Skip past CR and LF
CLC ;Indicate end of line found
AT_NEXT:
POP CX
RET
FIND_NEXT ENDP
;-----------------------------------------------------------------------
; This finds the offset of the start of the next paragraph. The search
; starts at ES:SI. On return CF=1 if no CR was found
;-----------------------------------------------------------------------
FIND_NEXT_PARA PROC NEAR
CALL FIND_NEXT ;Goto start of next line
JC FNP_EOF
FNP_LOOP:
CMP SI,LAST_CHAR ;At end of file?
JNB FNP_EOF
LODSB ;Get next character
CMP AL," " ;Is it a space?
JE FNP_LOOP
CMP AL,TAB ;Is it a TAB?
JE FNP_LOOP
CMP AL,CR
JNE FIND_NEXT_PARA
CMP SI,LAST_CHAR ;At end of file?
JNB FNP_EOF
LODSB
CMP AL,LF
JNE FIND_NEXT_PARA
;Now look next non-white space graphic
FNP_LOOP2:
CMP SI,LAST_CHAR ;At end of file?
JNB FNP_EOF
LODSB
FNP_LOOP3:
CMP AL," "
JE FNP_LOOP2
CMP AL,TAB
JE FNP_LOOP2
CMP AL,CR
JNE FNP_DONE
CMP SI,LAST_CHAR ;At end of file?
JNB FNP_EOF
LODSB
CMP AL,LF
JNE FNP_LOOP3
JMP SHORT FNP_LOOP2
FNP_DONE:
DEC SI
CLC
RET
FNP_EOF:
STC
RET
FIND_NEXT_PARA ENDP
;-----------------------------------------------------------------------
; This searches for the next carriage return in the file. The search
; starts at the offset in register SI.
;-----------------------------------------------------------------------
FIND_EOL PROC NEAR
MOV AL,CR ;Look for a carriage return
CR_SCAN:
MOV CX,LAST_CHAR ;Last letter in the file
SUB CX,SI ;Count for the search
MOV DI,SI
JCXZ AT_END ;If nothing to search, return
REPNE SCASB ;Scan for the character
MOV SI,DI ;Return the location of the CR
JCXZ AT_END ;If not found, return
CMP BYTE PTR [SI],LF
JNE CR_SCAN
DEC SI
CLC ;Indicate the CR was found
RET
AT_END:
STC ;Indicate CR was not found
RET
FIND_EOL ENDP
;-----------------------------------------------------------------------
; This subroutine reformats the current line
; and splits it so that the text fits on the screen
; returns carry = 1 if line split
;-----------------------------------------------------------------------
FORMAT_LINE PROC NEAR
PUSH UNDO_LENGTH
GET_LINE_LENGTH:
CALL FIND_START ;SI now points to start of line
PUSH SI
CALL FIND_EOL
MOV BX,SI ;BX now points to CR at EOL
POP SI
PUSH ES
PUSH CS
POP ES
ASSUME ES:CSEG
XOR CX,CX ;CX to hold line length
MOV LAST_WORD,SI ;save position of first word in line
MOV INDENT_SIZE,0
MOV DI,OFFSET INDENT_BUFFER
LOOP_TO_WORD:
CMP SI,BX ;End of Line?
JAE INDENT_END
LODSB
CMP AL,TAB ;Is this a TAB?
JNE NOT_LEADING_TAB
AND CL,0F8H ;Mask to round down to multiple of 8
ADD CX,8 ;Add tab count (tabs EVERY 8 CHARS)
JMP SHORT STORE_INDENT
NOT_LEADING_TAB:
INC CX
CMP AL," " ;is it a space?
JNE INDENT_END
STORE_INDENT:
CMP CL,MAX_INDENT ;check for overflow
JA LOOP_TO_WORD
STOSB ;store in indent buffer
INC INDENT_SIZE
JMP SHORT LOOP_TO_WORD
INDENT_END:
POP ES
ASSUME ES:NOTHING
NEW_WORD:
MOV AX,WCOLUMNS
INC AX
CMP CX,AX ;Past right edge of screen?
JA LOOP_END_WORD
MOV AX,SI
DEC AX
MOV LAST_WORD,AX ;Save position of start of this word
LOOP_END_WORD:
CMP SI,BX ;End of Line?
JAE EOL_FOUND
LODSB
CMP AL,TAB ;Is it a TAB?
JNE NOT_TAB
AND CL,0F8H ;Mask to round down to multiple of 8
ADD CX,8 ;Add tab count (tabs EVERY 8 CHARS)
JMP SHORT END_OF_WORD
NOT_TAB:
INC CX
CMP AL," " ;Is it a space?
JE END_OF_WORD
CMP AL,"-" ;Is it Hyphen
JNE LOOP_END_WORD
END_OF_WORD:
PUSH CX
LOOP_TO_NEXT_WORD:
CMP SI,BX ;End of Line?
JAE SPACE_AT_EOL
LODSB
CMP AL,TAB ;Is it a TAB?
JNE NOT_TRAILING_TAB
AND CL,0F8H ;Mask to round down to multiple of 8
ADD CX,8 ;Add tab count (tabs EVERY 8 CHARS)
JMP SHORT LOOP_TO_NEXT_WORD
NOT_TRAILING_TAB:
INC CX
CMP AL," " ;Is it a space?
JE LOOP_TO_NEXT_WORD
POP AX ;Adjust stack
JMP SHORT NEW_WORD
SPACE_AT_EOL:
POP AX ;Get line size at entry to last loop
CMP AX,WCOLUMNS ;Past right edge of screen
JBE FORMAT_END
EOL_FOUND:
CMP CX,WCOLUMNS ;Is line longer than screen width?
JA WORD_WRAP ;then reformat
FORMAT_END:
POP UNDO_LENGTH
CLC
RET
WORD_WRAP:
MOV AX,SI
SUB AX,LAST_WORD ;How long is last word?
CMP AX,CX ;As long as line?
JGE FORMAT_END ;Cannot wrap words as long as line
;insert CR/LF and indent at start of last word
PUSH SI
PUSH CURSOR
MOV DI,LAST_WORD
MOV CURSOR,DI
MOV AX,INDENT_SIZE
ADD AX,2 ;AX holds space requirement for CR/LF
;and indent
CMP MARK_MODE,0
JE OPEN_UP
CMP DI,MARK_HOME
JA OPEN_UP ;before mark_end
MOV BX,MARK_HOME
ADD BX,AX
MOV MARK_HOME,BX ;then update mark end
OPEN_UP:
PUSH AX
CALL OPEN_SPACE ;Make space for CR/LF
POP BX
POP CURSOR
POP SI
JC FORMAT_END ;exit if no space
ADD SI,BX ;update EOL pointer
MOV DI,LAST_WORD ;DI = CR/LF insertion point
MOV AX,LF*256+CR
STOSW ;insert CR/LF
MOV CX,INDENT_SIZE
JCXZ NO_INDENT
PUSH SI
MOV SI,OFFSET INDENT_BUFFER
PUSH DS
PUSH CS
POP DS
REP MOVSB ;Insert indent characters
POP DS
POP SI
NO_INDENT:
MOV DX,CUR_POSN
MOV DI,CURSOR
CMP DI,LAST_WORD ;was CURSOR after insertion point?
JB ADJUST_EOL
ADD DI,BX ;update CURSOR pointer
MOV CURSOR,DI
CMP DH,ROWS ;Bottom of screen?
JNB ADJUST_EOL
INC DH ;Otherwise, locate cursor on next row
ADJUST_EOL:
MOV SI,CURSOR
CALL LOCATE
MOV DIRTY_BITS,1 ;redraw screen
POP UNDO_LENGTH
STC
RET
FORMAT_LINE ENDP
;----------------------------------------------------------------------------
; This subroutine deletes the current end of line (w.r.t. SI) and following
; white space unless it is end of paragraph as well
;----------------------------------------------------------------------------
JOIN PROC NEAR
CALL JOIN_LINES
CMP WRAP_FLAG,0 ;wORD WRAP ON?
JE END_JOIN
CALL WRAP
END_JOIN:
RET
JOIN ENDP
;-----------------------------------------------------------------------
; Joins the current line only
; returns with carry set if at EOP
;--------------------------------------------------------------------------
JOIN_LINES PROC NEAR
MOV SI,CURSOR
CALL FIND_EOL ;Advance SI to EOL
JNC JOIN_NOT_AT_TOF
RET
JOIN_NOT_AT_TOF:
MOV DI,SI
DEL_EOL_SPACES:
OR DI,DI ;Top of file?
JZ DEL_EOL_DONE
DEC DI
CMP BYTE PTR [DI]," " ;Remove trailing space
JE DEL_EOL_SPACES
CMP BYTE PTR [DI],TAB ;and TABs
JE DEL_EOL_SPACES
INC DI
DEL_EOL_DONE:
;delete EOL marker and trailing white space
MOV CX,LAST_CHAR
INC SI
INC SI ;move pass CR/LF
LOOP_PASSED_SPACES:
CMP SI,CX ;at EOF?
JNB DEL_EOL_MARKER
LODSB
CMP AL,CR ;Is it a CR?
JNE TEST_FOR_SPACE
CMP SI,CX ;at EOF?
JNB DEL_EOL_MARKER
LODSB
CMP AL,LF ;Is it a LF?
JNE TEST_FOR_SPACE
STC ;indicates EOP on exit
RET
TEST_FOR_SPACE:
CMP AL," " ;Loop to remove leading spaces
JE LOOP_PASSED_SPACES
CMP AL,TAB ;and tabs
JE LOOP_PASSED_SPACES
DEC SI ;step back
DEL_EOL_MARKER:
SUB CX,SI ;Calculate no. of chars to move
MOV AX,CX
ADD AX,DI
MOV LAST_CHAR,AX ;Adjust file length
CMP DI,CURSOR ;CURSOR after deletion point?
JA CHECK_MARK_HOME
MOV CURSOR,DI ;adjust CURSOR
CHECK_MARK_HOME:
CMP MARK_MODE,0 ;Text Marked?
JE DEL_WHITE_SPACE
CMP DI,MARK_HOME ;MARK START after deletion?
JA DEL_WHITE_SPACE
MOV AX,SI
SUB AX,DI ;Calculate no. of chars to shift up
MOV BX,MARK_HOME
SUB BX,AX ;reduce MARK_START by deletion
MOV MARK_HOME,BX
DEL_WHITE_SPACE:
PUSH DI
REP MOVSB ;Move file down one notch
OR DIRTY_BITS,2 ;bottom of screen is dirty
POP DI ;get back old EOL pointer
CMP BYTE PTR [DI-1],"-" ;is hyphen at old EOL?
JE JOIN_DONE
MOV AX,1 ;otherwise insert space
PUSH CURSOR
MOV CURSOR,DI
PUSH DI
CALL OPEN_SPACE
POP DI
POP CURSOR
JNC JOIN_MOVED_UP
RET
JOIN_MOVED_UP:
CMP DI,CURSOR
JA AND_MARK_HOME
INC CURSOR
AND_MARK_HOME:
CMP MARK_MODE,0 ;Text Marked?
JE ADD_SPACE
CMP DI,MARK_HOME
JA ADD_SPACE
INC MARK_HOME
ADD_SPACE:
MOV AL," "
STOSB
JOIN_DONE:
CLC ;Indicates not EOP
RET
JOIN_LINES ENDP
;-----------------------------------------------------------------------
; This subroutine positions the screen with the cursor at the row
; selected in register DH. On entry, SI holds the cursor offset.
;-----------------------------------------------------------------------
LOCATE PROC NEAR
MOV CL,DH
XOR CH,CH
MOV CURSOR,SI
XOR DX,DX ;Start at top of the screen
OR SI,SI ;At start of buffer?
JZ LOCATE_FIRST
CALL FIND_START ;Get start of this row
XOR DX,DX ;Start at top of the screen
OR SI,SI ;Is cursor at start of file?
JZ LOCATE_FIRST
JCXZ LOCATE_FIRST ;If locating to top row were done
FIND_TOP:
PUSH SI
PUSH CX
CALL FIND_CR ;Find previous row
POP CX
POP AX
CMP BYTE PTR [SI],CR
JNE LOCATE_FIRST
CMP BYTE PTR [SI+1],LF
JNE LOCATE_FIRST
CMP SI,AX ;Did it change?
JE LOCATE_DONE ;If not, quit moving
INC DH ;Cursor moves to next row
LOOP FIND_TOP
LOCATE_DONE:
PUSH CURSOR
MOV CURSOR,SI
CALL FIND_START ;Find start of top of screen
POP CURSOR
LOCATE_FIRST:
MOV TOP_OF_SCREEN,SI
MOV CUR_POSN,DX
CALL CURSOR_COL
MOV SAVE_COLUMN,DL
RET
LOCATE ENDP
;-----------------------------------------------------------------------
; This subroutine computes the correct column for the cursor. No
; inputs. On exit, CUR_POSN is set and DX has the row/column.
;-----------------------------------------------------------------------
CURSOR_COL PROC NEAR
MOV SI,CURSOR ;Get cursor offset
CALL FIND_START ;Find start of this line
MOV CX,CURSOR
SUB CX,SI
MOV DX,CUR_POSN ;Get current row
XOR DL,DL ;Start at column zero
MOV MARGIN_COUNT,DL ;Count past the left margin
JCXZ COL_DONE
CURSOR_LOOP:
LODSB ;Get the next character
CMP AL,CR ;Is it the end of line?
JNE NOT_EOL
CMP BYTE PTR [SI],LF
JE COL_DONE ;If end, were done
NOT_EOL:
CMP AL,TAB ;Is it a tab?
JNE NOT_A_TAB
MOV BL,MARGIN_COUNT
OR BL,00000111B
MOV MARGIN_COUNT,BL
CMP BL,LEFT_MARGIN ;Inside visible window yet?
JB NOT_A_TAB ;If not, don't advance cursor
OR DL,00000111B ;Move to multiple of eight
NOT_A_TAB:
MOV BL,MARGIN_COUNT
INC BL
MOV MARGIN_COUNT,BL
CMP BL,LEFT_MARGIN
JBE OUT_OF_WINDOW
INC DL ;Were at next column now
OUT_OF_WINDOW:
LOOP CURSOR_LOOP
COL_DONE:
CMP DL,COLUMNSB ;Past end of display?
JB COLUMN_OK ;If not, were OK?
MOV DL,COLUMNSB
DEC DL ;Leave cursor at last column
COLUMN_OK:
MOV CUR_POSN,DX ;Store the row/column
RET
CURSOR_COL ENDP
;-----------------------------------------------------------------------
; This displays the string at CS:SI at the location in DX. The
; remainder of the row is erased. Cursor is put at the end of the line.
;-----------------------------------------------------------------------
TTY_STRING PROC NEAR
ASSUME DS:CSEG
PUSH DX
CALL POSITION ;Compute offset into video
POP DX
TTY_LOOP:
LODSB
OR AL,AL ;At end of string yet?
JZ TTY_DONE
INC DL
PUSH DX
CALL WRITE_INVERSE ;Write in inverse video
POP DX
JMP TTY_LOOP
TTY_DONE:
CALL SET_CURSOR ;Move cursor to end of string
JMP ERASE_EOL ;Erase the rest of line
TTY_STRING ENDP
;-----------------------------------------------------------------------
; This copies the input filename to CS:DI and changes the extension
;-----------------------------------------------------------------------
CHG_EXTENSION PROC NEAR
ASSUME DS:CSEG, ES:CSEG
PUSH SI
MOV SI,NAME_POINTER
CHG_LOOP:
LODSB
CMP AL,"." ;Look for the extension
JE FOUND_DOT
OR AL,AL
JZ FOUND_DOT
STOSB ;Copy a character
JMP CHG_LOOP
FOUND_DOT:
MOV CX,5 ;Five chars in extension
POP SI
REP MOVSB ;Move new extension in
RET
CHG_EXTENSION ENDP
;-----------------------------------------------------------------------
; This is the control break handler. It ignores the break.
;-----------------------------------------------------------------------
NEWINT23 PROC FAR
ASSUME DS:NOTHING, ES:NOTHING
MOV CS:DIRTY_BITS,1
CLC ;Tell DOS to ignore break
IRET
NEWINT23 ENDP
;-----------------------------------------------------------------------
; This is the severe error handler. It homes the cursor before
; processing the error.
;-----------------------------------------------------------------------
NEWINT24 PROC FAR
ASSUME DS:NOTHING, ES:NOTHING
PUSHF
PUSH AX
PUSH BX
PUSH DX
MOV CS:DIRTY_BITS,1
XOR DX,DX
CALL SET_CURSOR ;Put cursor at home
POP DX
POP BX
POP AX
POPF
JMP CS:OLDINT24
NEWINT24 ENDP
;-----------------------------------------------------------------------
EVEN
NAME_DOT_$$$ EQU $
NAME_DOT_BAK EQU $ + 80H
UNDO_BUFFER EQU $ + 100H
LINE_BUFFER EQU $ + 200H
NEW_STACK EQU $ + 500H
CSEG ENDS
;-----------------------------------------------------------------------
FILE_SEG SEGMENT
FILE_SEG ENDS
END START